# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time        : 2020/11/5 19:30
@Author      : Albert Darren
@Contact     : 2563491540@qq.com
@File        : ValidNumber.py
@Version     : Version 1.0.0
@Description : TODO 获得有效数字位数
@Created By  : PyCharm
"""
from decimal import Decimal, getcontext


# 自定义一个非法输入数字异常类
class IllegalNumber(Exception):
    def __init__(self, decimal_number):
        self.decimal_number = decimal_number

    def __str__(self):
        return "Illegal input:{},Not int or float".format(self.decimal_number)


def significant_figures(decimal_number: (int, float), precision=30):
    """
    返回指定十进制数字有效数字位数，不包括科学计数法表示的数字
    :param precision: 准确值精度
    :param decimal_number: integer or float
    :return: the number of significant digits in a decimal number.
    """
    # getcontext().prec=precision
    # accuracy_value=Decimal((-1+pow(5, 0.5))/2)
    # absolute_error_limit=1

    # 将decimal_number转化为字符串类型
    decimal_number = str(decimal_number)
    # 输入合法性判断,eval内置函数可以检查部分非法情况
    if isinstance(eval(decimal_number), (int, float)):
        decimal_number = decimal_number.lstrip('-.0')  # 去掉最左边的负号-,小数点.和无效的0
        # 获得当前十进制数的长度
        sig_digits = len(decimal_number)
        # 判断小数点是否在sig_digits中
        if "." in decimal_number:
            # 小数点在sig_digits中就减去1
            sig_digits -= 1
        return sig_digits
    else:
        # 输入不合法，抛出eval不能检出的异常
        raise IllegalNumber(decimal_number)


def iterate_positive_root(x0=1, iteration_depth=5):
    """
    迭代法求一元二次方程正根的近似解x*
    :param x0: 初始近似值,默认为1
    :param iteration_depth: 迭代次数或深度,默认是5次
    :return: 方程的近似解
    """
    x = x0
    for i in range(iteration_depth):
        x = 1 / (1 + x)
    return x0, iteration_depth, x


def iterate_sqrt(radicand: (int, float), initial_value: (int, float), precision: float = -9):
    """
    返回在指定精度和初始近似值下的被开方数的平方根
    :param radicand:被开方数
    :param initial_value:初始近似值x0
    :param precision:迭代计算精度，用幂指数表示精度，默认为-9，即10的﹣9次方或者1e-09
    :return:the square root of radicand
    """
    # 默认高精度浮点运算为28位,包括整数位和小数位,设置准确值的位数
    getcontext().prec = abs(precision) + 2
    sqrt = initial_value
    # 准确值
    accuracy_value = pow(Decimal(radicand), Decimal(0.5))
    # 绝对误差
    absolute_error = 0.5 * pow(10, precision)
    # 计数器，统计迭代次数
    count = 0
    while 1:
        # 迭代误差
        error = abs(accuracy_value - Decimal(sqrt))
        if error < absolute_error:
            return radicand, initial_value, count, accuracy_value, sqrt
        sqrt = 0.5 * (sqrt + radicand / sqrt)
        count += 1


"""
    abs(0.6153846153846154-0.6180339887498948)
0.0026493733652793727
    """

if __name__ == '__main__':
    result = iterate_positive_root()
    print("""
初始近似值x0={},
迭代{}次，
得到该方程的正根近似解x*={}的有效数字位数为:{}
    """.format(result[0], result[1], result[2], significant_figures(str(result[2]))))
    # 准确值：0.61803398874989484820458683436564
    # 近似值：0.6180339887498948
    # 下面是函数significant_figures的调试测试
    # answer = input("请输入十进制数字:")
    # while 1:
    #     print("数字{}的有效数字位数为:{}".format(answer, significant_figures(answer)))
    #     answer = input("请输入十进制数字或者按enter键结束:")
    #     if answer == "":
    #         break
